﻿module game.battle.entity;

import game.units;

class Entity
{
private:
    pos_t mPos    = ZeroPos;
    rot_t mDir    = ZeroRot;
    pos_t mSpd    = ZeroPos;
    rot_t mRotSpd = ZeroRot;

public:
    @property pos_t pos()      const pure nothrow { return mPos; }
    @property rot_t dir()      const pure nothrow { return mDir; }
    @property pos_t speed()    const pure nothrow { return mSpd; }
    @property rot_t rotSpeed() const pure nothrow { return mRotSpd; }

    @property void pos(in pos_t p)      pure nothrow { mPos = p; }
    @property void dir(in rot_t d)      pure nothrow { mDir = d; }
    @property void speed(in pos_t s)    pure nothrow { mSpd = s; }
    @property void rotSpeed(in rot_t s) pure nothrow { mRotSpd = s; }

    final void accelerate(in scalar_t spd) pure nothrow 
    {
        mSpd += mDir * spd;
    }

    final void strafe(in scalar_t spd) pure nothrow
    {
        mSpd += (mDir * toRotMat(Pi2Rot)) * spd;
    }

    void update(bool inertialess, T)(in T controller) pure nothrow
    {
        import std.math: PI;
        assert(controller.rotAcceleration < cast(scalar_t)PI);
        assert(controller.rotDeceleration < cast(scalar_t)PI);
        assert(controller.maxRotSpeed     < cast(scalar_t)PI);
        static if(inertialess)
        {
            if(controller.up)
            {
                speed = dir * controller.maxSpeed;
            }
            else if(controller.down)
            {
                speed = dir * -controller.maxSpeed;
            }
            else if(controller.strafe && controller.left)
            {
                speed = dir * toRotMat(Pi2Rot) * controller.maxSpeed;
            }
            else if(controller.strafe && controller.right)
            {
                speed = dir * toRotMat(Pi2Rot) * -controller.maxSpeed;
            }
            else
            {
                speed = ZeroPos;
            }

            if(!controller.strafe && controller.left)
            {
                dir = dir * toRotMat(controller.maxRotSpeedV);
            }
            else if(!controller.strafe && controller.right)
            {
                dir = dir * toRotMat(controller.maxRotSpeedV).transposed;
            }

            pos = pos + speed;
            mDir.normalize();
        }
        else
        {
            auto spdSq = speed.magnitude_squared;
            if(controller.up && spdSq < controller.maxSpeedSquared)
            {
                accelerate(controller.acceleration);
            }
            else if(controller.down && spdSq < controller.maxSpeedSquared)
            {
                accelerate(-controller.acceleration);
            }
            if(controller.strafe && controller.left && spdSq < controller.maxSpeedSquared)
            {
                strafe(controller.acceleration);
            }
            else if(controller.strafe && controller.right && spdSq < controller.maxSpeedSquared)
            {
                strafe(-controller.acceleration);
            }
            else
            {
                auto spd = speed.magnitude;
                if(spd > controller.deceleration)
                {
                    speed = speed * ((spd - controller.deceleration) / spd);
                }
                else
                {
                    speed = ZeroPos;
                }
            }

            auto rotSgn = cast(scalarw_t)rotSpeed.x * cast(scalarw_t)rotSpeed.y;
            if(!controller.strafe && controller.left && (rotSgn < 0 || rotSpeed.x > controller.maxRotSpeedV.x))
            {
                rotSpeed = rotSpeed * toRotMat(controller.rotAccelerationV);
            }
            else if(!controller.strafe && controller.right && (rotSgn > 0 || rotSpeed.x > controller.maxRotSpeedV.x))
            {
                rotSpeed = rotSpeed * toRotMat(controller.rotAccelerationV).transposed;
            }
            else
            {
                if(rotSpeed.x < controller.rotDecelerationV.x)
                {
                    if(rotSgn > 0)
                    {
                        rotSpeed = rotSpeed * toRotMat(controller.rotDecelerationV).transposed;
                    }
                    else
                    {
                        rotSpeed = rotSpeed * toRotMat(controller.rotDecelerationV);
                    }
                }
                else
                {
                    rotSpeed = ZeroRot;
                }
            }
            rotSpeed = rotSpeed.normalized;

            pos = pos + mSpd;
            auto mat = toRotMat(rotSpeed);
            mDir = mat * mDir;
            mDir.normalize();
        }
    }

}

